#include <assert.h>
#include <memory.h> 
#include <string.h>

#include "fastcgi.hpp"
#include "aconnect/util.string.hpp"

namespace fastcgi 
{	

	aconnect::string fastcgi_error::formatErrorMessage (aconnect::err_type errorCode)
	{
#ifdef WIN32
			return aconnect::util::formatWindowsError(errorCode);
#else
			char buff[32] = {0,};
			snprintf (buff, 32, "Error code: 0x%08X", errorCode);
			return buff;
#endif
	}
	
	namespace support
	{

	void formatFcgiHeader (Header &header, byte_type type, int requestId, int contentLength, byte_type paddingLength)
	{
		assert(contentLength >= 0 && (size_t) contentLength <= MaxMessageContentLength);
		assert(paddingLength >= 0 && paddingLength <= 0xff);

		header.version			= FcgiVersion1;
		header.type             = type;
		header.requestIdB1      = ((requestId     >> 8) & 0xff);
		header.requestIdB0      = ((requestId         ) & 0xff);
		header.contentLengthB1  = ((contentLength >> 8) & 0xff);
		header.contentLengthB0  = ((contentLength     ) & 0xff);
		header.paddingLength    = paddingLength;
		header.reserved         =  0;
	}


	void formatBeginRequestBody (
		BeginRequestBody &body,
        int role,
        bool keepConnection)
	{
		
		assert ((role >> 16) == 0);

		body.roleB1 = (byte_type) ((role >>  8) & 0xff);
		body.roleB0 = (byte_type) (role         & 0xff);

		body.flags  = (keepConnection ? FcgiBeginRequestFlags::KeepConnection : 0);
		memset(body.reserved, 0, sizeof(body.reserved));
	}

	size_t getKeyValuePairLength (const char* name, const char* value)
	{
		size_t nameLen = name ? strlen (name) : 0;
		size_t valueLen = value ? strlen (value) : 0;

		size_t res = 2 + nameLen + valueLen;
		
		if (nameLen >= 0x80)
			res += 3;

		if (valueLen >= 0x80)
			res += 3;

		return res;
	}

	int writeKeyValuePair (byte_type* buff, int buffSize, const char* name, const char* value)
	{
		assert (buff);
		assert (buffSize > 0);
		assert (name);

		size_t nameLen = (name ? strlen (name) : 0), 
			valueLen = 0;

		assert(nameLen > 0);
		
		int fullLength = getKeyValuePairLength(name, value);
		
		if (buffSize < fullLength) // not enough place in buffer
			return -(fullLength - buffSize);

		byte_type *startBuffPtr = buff;
		
		if (nameLen < 0x80) {
			*buff++ = (byte_type) nameLen;

		} else {
			*buff++ = (byte_type) ((nameLen >> 24) | 0x80);
			*buff++ = (byte_type) (nameLen >> 16);
			*buff++ = (byte_type) (nameLen >> 8);
			*buff++ = (byte_type) nameLen;
		}
		
		if (value) 
			valueLen = strlen (value);

		if (valueLen < 0x80) {
			*buff++ = (byte_type) valueLen;
		} else {
			*buff++ = (byte_type) ((valueLen >> 24) | 0x80);
			*buff++ = (byte_type) (valueLen >> 16);
			*buff++ = (byte_type) (valueLen >> 8);
			*buff++ = (byte_type) valueLen;
		}
	

		// place name-value

		memcpy (buff, name, nameLen);
		buff += nameLen;

		if (valueLen > 0) {
			memcpy (buff, value, valueLen);
			buff += valueLen;
		}

		return buff - startBuffPtr;
	}

	aconnect::string formatKeyValuePairs (aconnect::str2str_map pairs)
	{
		aconnect::str_stream res;
		aconnect::str2str_map::const_iterator iter = pairs.begin();
		
		while(iter != pairs.end()) {
			
			size_t nameLen = iter->first.size();
			size_t valueLen = iter->second.size();

			if (nameLen < 0x80) {
				res << (aconnect::char_type) nameLen;
			} else {
				res << (aconnect::char_type) ((nameLen >> 24) | 0x80);
				res << (aconnect::char_type) (nameLen >> 16);
				res << (aconnect::char_type) (nameLen >> 8);
				res << (aconnect::char_type) nameLen;
			}
			
			if (valueLen < 0x80) {
				res << (aconnect::char_type) valueLen;
			} else {
				res << (aconnect::char_type) ((valueLen >> 24) | 0x80);
				res << (aconnect::char_type) (valueLen >> 16);
				res << (aconnect::char_type) (valueLen >> 8);
				res << (aconnect::char_type) valueLen;
			}

			res << iter->first;

			if (valueLen)
				res << iter->second;

			++iter;
		}

		return res.str();
	}


	aconnect::str2str_map parseKeyValuePairs(const byte_type* buff, size_t length) throw (fastcgi_error)
	{
		aconnect::str2str_map values;
		
		if (length == 0)
			throw fastcgi_error(FcgiError::ProtocolError, "FastCGI key-value pair name length not found");

		int nameLen, valueLen;
		const byte_type* currentPos = buff;
		const byte_type* bufferEnd = buff + length;

		nameLen = *currentPos++;
		
		// Read name length (one or four bytes) and value length (one or four bytes) from stream.
		if((nameLen & 0x80) != 0) {
			if(bufferEnd - currentPos < 3)
				throw fastcgi_error(FcgiError::ProtocolError, "FastCGI key-value pair name length corrupted");
			
			nameLen = ((nameLen & 0x7f) << 24) 
				+ (currentPos[0] << 16) 
				+ (currentPos[1] << 8) 
				+ currentPos[2];

			currentPos += 3;
		}

		if (currentPos >= bufferEnd)
			throw fastcgi_error(FcgiError::ProtocolError, "FastCGI key-value pair value length not found");

		valueLen = *currentPos++;
		
		if((valueLen & 0x80) != 0) {
			if(bufferEnd - currentPos < 3)
				throw fastcgi_error(FcgiError::ProtocolError, "FastCGI key-value pair value length corrupted");
			
			valueLen = ((valueLen & 0x7f) << 24) 
				+ (currentPos[0] << 16)
				+ (currentPos[1] << 8) 
				+ currentPos[2];

			currentPos += 3;
		} 


		// nameLen and valueLen are now valid; read the name and value
		// from stream and construct a standard environment entry.

		if(bufferEnd - currentPos < nameLen + valueLen)
			throw fastcgi_error(FcgiError::ProtocolError, "FastCGI key-value pair data length corrupted");

		aconnect::string name ((aconnect::string_constptr) currentPos, nameLen);
		values[name] = aconnect::string ((aconnect::string_constptr) currentPos + nameLen, valueLen);
		
		return values;
	}
	
}} // fastcgi::support

